
local oo = require("SharedLibs.BaseLib")
local pt = require("SharedLibs.Prototype")

TickedQueue = require("SharedLibs.Algorithem.TickedQueue");
Debugger = require("SharedLibs.Debugger");
-- co = require(sharedLibs.CoroLib);
Signal = require("SharedLibs.Signal.Signal");
Binaryheap = require("SharedLibs.Algorithem.binaryHeap");

local _IsClient = Debugger.IsClient;

local Timer = pt.proto("Timer");

function Timer:constructor(time, handler, startTime)
	self.time = time or 0;
	self.startTime = startTime;
	self.handler = handler;
end

function Timer:Cancel()
	self.isCanceled = true;
end

--[[
	支持两类Timer:
	1. 对于时间长，数量特别的Timer，请使用SetTimer(handler)
	2. 对于需要Coro支持的（可以Pause），请使用co.NewDelay(), co.Delay()
]]
local cls = oo.class();

cls.currentTime = os.clock();
cls.deltaTime = 0.015;
cls.iFrame = 0;

cls.ticked = Signal.new("ticked");
if _IsClient then
	cls.renderTicked = Signal.new("renderTicked"); 
end

cls._tickedQueue = TickedQueue.new();

--- 目前仅用于对RawBlocked进行保护, 详见RPC:__call, RawWait等函数
cls._InTicking = false;

local _heap = Binaryheap.minUnique();

function cls.Enqueue(ticker)
	cls._tickedQueue:Enqueue(ticker);
end

--[[
	Can use timer:Cancel() to cancel a Timer
	如果time为0, 那么会延迟一帧执行
 	注意：这个Timer无法Pause！
]]
function cls.SetTimer(time, timerHandler, userData)
	time = time or 0.000001;
	local curTime = cls.currentTime;	
	local timer = Timer.new(time, timerHandler, curTime);
	
	_heap:insert(timer, curTime + time);
	return timer;
end

local function _updateTimers()
	local timer = _heap:peek();
	if not timer then
		return
	end

	local start = cls.currentTime;
	while true do
		local elapsedTime = start - timer.startTime;

		if  elapsedTime >= timer.time then
			if not timer.isCanceled then
				timer:handler();
			end

			_heap:pop();

			timer = _heap:peek();
			if not timer then 
				break 
			end
		else
			break
		end		
	end
end	

-- RunService = game:GetService('RunService');
-- RunService.Stepped:Connect(function(currentTime, deltaTime)
-- 	cls._InTicking = true;
-- 	_updateTimers();	
-- 	cls._tickedQueue:Update();
	
-- 	cls.currentTime = currentTime;
-- 	cls.deltaTime = deltaTime;
-- 	cls.ticked:Emit(currentTime);
-- 	cls.iFrame = cls.iFrame + 1;
-- 	cls._InTicking = false;
-- end)

-- if _IsClient then
-- 	RunService.RenderStepped:Connect(function(deltaTime)
-- 		cls._InTicking = true;		
-- 		cls.renderTicked:Emit(deltaTime);	
-- 		cls._InTicking = false;
-- 	end)
-- end

function cls.ManuallyTick(currentTime, deltaTime)
	cls._InTicking = true;

	_updateTimers();
	cls._tickedQueue:Update();
	
	cls.currentTime = currentTime;
	cls.deltaTime = deltaTime;
	cls.ticked:Emit(currentTime);

	cls._InTicking = false;
end

return cls;
